/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "yyjson.h"
#include "json_error.h"
#include "yyjson_util.h"
#include "securec.h"
#include "json_implement.h"

using namespace json_error;
using namespace yyjson_util;

napi_value json_implement::ParseJson(napi_env env, const char *jsonStr, size_t len, uint32_t flag)
{
    yyjson_read_err err;
    yyjson_read_flag flg = flag | GetDefaultReadFlag();
    unique_yyjson_doc doc(yyjson_read_opts((char*)jsonStr, len, flg, NULL, &err));
    if (doc.get() == nullptr) {
        HANDLE_YYJSON_ERROR_WITH_READ_ERR(env, json_error::NO_YYJSON_ERROR_JSON_STR_INVALID, jsonStr, len, err);
    }
    yyjson_val *root = yyjson_doc_get_root(doc.get());
    return YYJSONToNapi(env, root, DEFAULT);
}

napi_value json_implement::ParseInc(napi_env env, char *data, size_t byteLength, uint32_t flag)
{
    yyjson_read_flag flg = flag | GetDefaultReadFlag();
    unique_yyjson_incr_state state(yyjson_incr_new(data, byteLength, flg, NULL));
    if (state.get() == nullptr) {
        HANDLE_YYJSON_ERROR(env, YYJSON_CREATE_STATE_FAILED);
    }
    
    unique_yyjson_doc doc;
    yyjson_read_err err;
    size_t readSoFar = 0;
    size_t blockSize = yyjson_util::GetBlockSize(byteLength);
    do {
        readSoFar += blockSize;
        if (readSoFar > byteLength) {
            readSoFar = byteLength;
        }
        doc.reset(yyjson_incr_read(state.get(), readSoFar, &err));
        if (err.code == YYJSON_READ_SUCCESS) {
            break;
        }
        if (err.code != YYJSON_READ_ERROR_MORE) {
            HANDLE_YYJSON_ERROR_WITH_READ_ERR(env, json_error::NO_YYJSON_ERROR_JSON_STR_INVALID, data, byteLength, err);
        }
    } while (readSoFar < byteLength);
    
    if (doc.get() == nullptr) {
        HANDLE_YYJSON_ERROR_WITH_READ_ERR(env, json_error::NO_YYJSON_ERROR_JSON_STR_INVALID, data, byteLength, err);
    }
    
    yyjson_val *root = yyjson_doc_get_root(doc.get());
    return YYJSONToNapi(env, root, DEFAULT);
}

static std::unique_ptr<char, FreeDeleter> StringifyToCString(napi_env env, napi_value value, uint32_t flag,
                                                             yyjson_write_err &err)
{
    unique_yyjson_mut_doc doc(yyjson_mut_doc_new(nullptr));
    
    yyjson_mut_val *root = NapiToYYJSON(env, doc.get(), value);
    yyjson_mut_doc_set_root(doc.get(), root);
    
    yyjson_write_flag flg = flag | GetDefaultWriteFlag();
    std::unique_ptr<char, FreeDeleter> json(yyjson_mut_write_opts(doc.get(), flg, nullptr, nullptr, &err));
    return json;
}

napi_value json_implement::Stringify(napi_env env, napi_value value, uint32_t flag)
{
    yyjson_write_err err;
    std::unique_ptr<char, FreeDeleter> json = StringifyToCString(env, value, flag, err);
    if (json.get() == nullptr) {
        HANDLE_YYJSON_ERROR_WITH_WRITE_ERR(env, err);
    }
    
    napi_value result;
    HANDLE_NAPI_ERROR(env, napi_create_string_utf8(env, json.get(), NAPI_AUTO_LENGTH, &result));
    return result;
}

napi_value json_implement::StringifyToBuffer(napi_env env, napi_value value, uint32_t flag)
{
    yyjson_write_err err;
    std::unique_ptr<char, FreeDeleter> json = StringifyToCString(env, value, flag, err);
    if (json.get() == nullptr) {
        HANDLE_YYJSON_ERROR_WITH_WRITE_ERR(env, err);
    }

    size_t jsonLen = strlen(json.get());
    napi_value arrayBuffer;
    void* bufferData;
    HANDLE_NAPI_ERROR(env, napi_create_arraybuffer(env, jsonLen, &bufferData, &arrayBuffer));
    if (memcpy_s(bufferData, jsonLen, json.get(), jsonLen) != EOK) {
        HandleBoundsCheckError(env, MEMCPY_S_ERROR, "memcpy_s failed while copying string to arraybuffer.");
    }
    return arrayBuffer;
}